//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_BUFFERED_PTR_H
#define _UXS_BUFFERED_PTR_H

#pragma once

#include "../Types/TAutoPtr.h"
#include "../Types/MemFunc.h"

namespace UX_STL
{
namespace UXTypes
{

	//
	// Buffered Pointer
	//

#	define TBufferedPtr		BufferedPtr

	template <typename T, usize BS = sizeof(T)>
	struct TBufferedPtr
	{
	// types
	public:
		typedef TBufferedPtr< T, BS >		Self;
		typedef T							value_t;

		static const usize					BUFFER_SIZE	= CompileTime::Max< usize, BS, sizeof(void *)*2 >::value;
		

	// variables
	private:
		T *			_ptr;
		union {
			char	_buf[ BUFFER_SIZE ];
			void *	_pointers[2];
		};


	// methods
	private:
		explicit TBufferedPtr (const TBufferedPtr &) {}

		RVALUE_REFERENCE_ONLY(
			TBufferedPtr (TBufferedPtr &&) {}
		)

		void operator = (const TBufferedPtr &) {}

	public:
		TBufferedPtr () : _ptr(null)
		{
			ZeroMem( _buf );
		}
		

		~TBufferedPtr ()
		{
			_Delete();
		}


		T * operator -> () const
		{
			ASSUME( _ptr != null );
			return _ptr;
		}


		T & operator * () const
		{
			ASSUME( _ptr != null );
			return *_ptr;
		}


		T * ptr ()
		{
			ASSUME( _ptr != null );
			return _ptr;
		}


		const T * const ptr () const
		{
			ASSUME( _ptr != null );
			return _ptr;
		}


		bool Empty () const
		{
			return ( _ptr == null );
		}


		bool IsNull () const
		{
			return ( _ptr == null );
		}


		bool IsNotNull () const
		{
			return not IsNull();
		}

		
		bool operator ! () const
		{
			return _ptr != null;
		}
		

	public:
		/*
			example:
				BufferredPtr<T> t;
				t._SetPtr( new( t._GetBuffer< sizeof(T) >() ) T() );
		*/

		template <usize Size>
		void * _GetBuffer ()
		{
			DEBUG_ONLY(
				CoreTypesStatistic::BufferedPtrMaxBufferSize = UXMath::Max( CoreTypesStatistic::BufferedPtrMaxBufferSize, Size );
			)

			_Delete();

			if ( Size <= BUFFER_SIZE )
				return _buf;
			
			void * p = _alloc( Size );

			_pointers[0] = this;
			_pointers[1] = p;

			WARNING( "static buffer is too small" );
			return p;
		}


		void _SetPtr (T *ptr)
		{
			ASSERT( _CmpPtr( ptr ) );
			_ptr = ptr;
		}


	private:
		void _Delete ()
		{
			if ( _ptr == null )
				return;

			_ptr->~T();
			_ptr = null;

			if ( _IsUsedDynamicBuffer() )
				_dealloc( _pointers[1] );

			ZeroMem( _buf );
		}


		bool _IsUsedDynamicBuffer () const
		{
			return _pointers[0] == this;
		}


		bool _CmpPtr (T *ptr) const
		{
			if ( _IsUsedDynamicBuffer() )
				return ComparePointers( _pointers[1], ptr );
			else
				return ComparePointers( _buf, ptr );
		}
	};

#	undef TBufferedPtr

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_BUFFERED_PTR_H
